package com.breeze.components.task.xxl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.breeze.components.task.xxl.model.XxlJobGroup;
import com.breeze.components.task.xxl.model.XxlJobInfo;
import com.breeze.components.task.xxl.service.JobGroupService;
import com.breeze.components.task.xxl.service.JobInfoService;
import com.xxl.job.core.executor.XxlJobExecutor;
import com.xxl.job.core.handler.impl.MethodJobHandler;
import jakarta.annotation.Resource;
import java.lang.reflect.Method;
import java.time.temporal.ChronoUnit;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.CronTask;
import org.springframework.scheduling.config.IntervalTask;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.config.Task;
import org.springframework.scheduling.support.ScheduledMethodRunnable;

/**
 * spring任务自动注册到xxl-job
 *
 * @author breeze
 */
@Slf4j
public class JobAutoRegister implements SchedulingConfigurer {

  private final NoOpScheduler noOpScheduler;
  @Resource private JobGroupService jobGroupService;
  @Resource private JobInfoService jobInfoService;
  @Resource private ApplicationContext applicationContext;

  public JobAutoRegister(NoOpScheduler noOpScheduler) {
    this.noOpScheduler = noOpScheduler;
  }

  @Override
  public void configureTasks(@NonNull ScheduledTaskRegistrar taskRegistrar) {
    if (jobGroupService.preciselyCheck() || jobGroupService.autoRegisterGroup()) {
      log.info("spring scheduled job is not empty, start to sync jobs...");
      try {
        List<XxlJobGroup> jobGroups = jobGroupService.getJobGroup();
        XxlJobGroup xxlJobGroup = jobGroups.getFirst();
        Stream.of(
                taskRegistrar.getCronTaskList(),
                taskRegistrar.getFixedRateTaskList(),
                taskRegistrar.getFixedDelayTaskList(),
                taskRegistrar.getTriggerTaskList())
            .flatMap(Collection::stream)
            .toList()
            .forEach(task -> registerTask(xxlJobGroup, task));
        log.info("spring scheduled job is not empty, sync jobs finished.");
      } catch (Exception e) {
        log.info("spring scheduled job is not empty, sync jobs failed.", e);
        throw new RuntimeException(e);
      }
    }
    taskRegistrar.setScheduler(noOpScheduler);
  }

  private void registerTask(XxlJobGroup xxlJobGroup, Task task) {
    if (task.getRunnable() instanceof ScheduledMethodRunnable runnable) {
      String className = AopUtils.getTargetClass(runnable.getTarget()).getSimpleName();
      // 自动注册到xxl-job
      String handlerName = String.join(StrUtil.DOT, className, runnable.getMethod().getName());
      // 注册xxl-job的任务
      registerJobHandler(handlerName, runnable.getMethod());
      // 因为是模糊查询，需要再过滤一次
      Optional<XxlJobInfo> first =
          jobInfoService.getJobInfo(xxlJobGroup.getId(), handlerName).stream()
              .filter(xxlJobInfo -> xxlJobInfo.getExecutorHandler().equals(handlerName))
              .findFirst();
      XxlJobInfo xxlJobInfo = convertToXxlJob(xxlJobGroup, task, handlerName);
      if (first.isEmpty() && Objects.nonNull(xxlJobInfo)) {
        String jobInfoId = jobInfoService.addJobInfo(xxlJobInfo);
        if (StrUtil.isNotEmpty(jobInfoId)) {
          log.info(
              ">>>>>>>>>>> xxl-job auto add jobInfo success! JobInfoId[{}] JobInfo[{}]",
              jobInfoId,
              JSON.toJSONString(xxlJobInfo));
        }
      }
    }
  }

  private XxlJobInfo convertToXxlJob(XxlJobGroup xxlJobGroup, Task task, String handlerName) {
    if (task instanceof CronTask cronTask) {
      return XxlJobInfo.buildJobInfo(
          xxlJobGroup.getId(), handlerName, "CRON", cronTask.getExpression());
    }
    if (task instanceof IntervalTask intervalTask) {
      long interval = intervalTask.getIntervalDuration().get(ChronoUnit.MILLIS) / 1000;
      return XxlJobInfo.buildJobInfo(
          xxlJobGroup.getId(), handlerName, "FIX_RATE", String.valueOf(Math.max(interval, 1)));
    }
    return null;
  }

  /**
   * 注册任务到xxl-job上
   *
   * @param handlerName JobHandler名称
   * @param executeMethod 执行定时任务的方法
   */
  private void registerJobHandler(String handlerName, Method executeMethod) {
    try {
      executeMethod.setAccessible(true);
      // 获取方法的Bean对象
      Object bean = applicationContext.getBean(executeMethod.getDeclaringClass());
      XxlJobExecutor.registJobHandler(
          handlerName, new MethodJobHandler(bean, executeMethod, null, null));
    } catch (Exception e) {
      log.error(">>>>>>>>>>> xxl-job register registerJobHandler error.", e);
    }
  }
}
